package scalab.core.gates

import utils.AlgorithmFactory
import component.{ExecutableComponent, Component, SinkComponent}
import dependencies.Dependency

trait Input extends Recipient{
	import scala.collection.immutable.{Set, HashSet}

	private var suppliers: List[Component] = Nil
	final def providers: List[Component] = suppliers
	//def allSourceComponents: Iterable[Component] = sources map {s => s.component}

	private[core] def updateId: String = {
		import scala.collection.immutable.TreeSet
		import scalab.utils.MD5
		component.identifier = MD5.digest(new TreeSet[String] ++ (providers map {p =>
			p.output.computeComponentId
		}))
		component.warning(component.identifier)
		component.identifier
	}

	//this is to override source behavior when working
	//with relays (e.g. Blackbox)
	//implement later...
	/*
	final override def upToDate: Boolean = upToDate(Nil)._1

	final override private[core] def upToDate(fresh: List[Buildable]): Pair[Boolean, List[Buildable]] = 
	if(fresh contains this)
		(true, fresh)
	else{
		var newFresh : List[Buildable] = fresh
		((providers map {p => p.output}) forall {e =>
			//check for fresh here? not that useful...
			val (toDate, moreFresh) = e upToDate fresh
			if(toDate)
				newFresh = newFresh union moreFresh
			toDate
		}, newFresh)
	}
	final override private[core] def locallyUpToDate: Boolean = true*/

	final override private[core] def upToDate(
		products: Set[Resource],
		visited: Set[Component], 
		cycles: Set[(Output, Input)], 
		fresh: Set[ExecutableComponent]): (
			Boolean, 
			Set[Component], 
			Set[(Output, Input)], 
			Set[ExecutableComponent]
		) = utils.AlgorithmFactory.createUpToDate(providers)(products, visited, cycles, fresh)

	//override Output's validate in trait Relay
	/*override private[core] def validate(validated: Set[Buildable]): Pair[Boolean, Set[Buildable]] = {
		//Report.debug("Sink", "validate")
		AlgorithmFactory.createValidate(providers)(validated)
		//providers forall {p => p.out.validate}
	}*/

	override private[core] def validate(
		visited: Set[Component], 
		cycles: Set[(Output, Input)], 
		validated: Set[SinkComponent]): (
			Boolean, 
			Set[Component], 
			Set[(Output, Input)], 
			Set[SinkComponent]
		) = utils.AlgorithmFactory.createValidate(providers)(visited, cycles, validated)

	//find a better name?
  final private[core] def flowsOut(out: Output): Input = {
		//do we need to insert pipes here???
    suppliers = out.component :: suppliers
    this
  }

	/*def <<(ps: Iterable[Provider]): Unit = {
		ps foreach {p => p->this}
	}

	def <<(ps: Provider*): Unit = <<(ps)*/

	//val component: Component

	final def sources: Iterable[Dependency] = providers flatMap {p => p.productDependencies}
	//def dependencies: Iterable[Dependency] = providers flatMap {p => p.productDependencies}
	//def orderedDependencies = Dependency.orderDependencies(dependencies)

	//def hasUniverse: Boolean = providers exists {p => p == Universe}

	//rename to lackingSources
	final def lackingSources: List[Dependency] = 
		(sources filter {d => d.lacks}).toList

	/*final override private[core] def update(fresh: Set[ExecutableComponent]): Pair[Boolean, Set[ExecutableComponent]] = {
		utils.AlgorithmFactory.createUpdate(providers map {p => p.output})(fresh)
	}*/

	final override private[core] def update(
		visited: Set[Component], 
		cycles: Set[(Output, Input)], 
		fresh: Set[ExecutableComponent]): (
			Boolean, 
			Set[Component], 
			Set[(Output, Input)], 
			Set[ExecutableComponent]
		) = {
			fireUpdateStarted
			val res = utils.AlgorithmFactory.createUpdate(providers)(visited, cycles, fresh)
			fireUpdateFinished(res._1)
			res
		}

	/*private[core] override def update: Boolean = {
    //do this elsewhere (a sink is not a source => no deps)
    //var dependencies: List[Dependency] = Nil //sure? use memory then
    sources forall { s =>
      val res = s.update
      component.out.setDeps(component.productDependencies ++ s.productDependencies)//why?????????????
      res
    }
  }*/

	private var outOfDateSrcs: Set[Dependency] = HashSet.empty[Dependency]

	
	final def outOfDateSources: Set[Dependency] = outOfDateSrcs

	final def invalidate(dep: Dependency): Recipient = {
		//component.info("invalidate: " + dep)
		outOfDateSrcs = outOfDateSrcs + dep
		this
	}

	final def invalidate(deps: Iterable[Dependency]): Input = {
		//component.info("invalidate: " + deps)
		outOfDateSrcs = outOfDateSrcs ++ deps
		this
	}

	//def invalidate: Input = invalidate(sources)
}


//class Input(override val component: Component) extends Sink


